#pragma once
#include "Log.hpp"
#include "data.hpp"
#include "httplib.h"

namespace my_cloud{
    class Server{
    private:
        int _server_port;
        std::string _server_ip;
        httplib::Server _server;
        std::string _download_prefix;
    private:
        static void Upload(const httplib::Request &req, httplib::Response &rsp){

            cout << "Upload" << endl;


            auto ret = req.has_file("file");
            if(ret == false){
                rsp.status = 400;
                return;
            }
            const auto& file = req.get_file_value("file");
            std::string realpath = Config::GetInstance()->GetBackDir() + FileUtil(file.filename).FileName();
            FileUtil fu(realpath);
            fu.SetContent(file.content);
            BackupInfo info;
            info.NewBackupInfo(realpath);
            DataManager::GetInstance()->Insert(info);
            return;
        }
        static std::string TimetoStr(time_t t){
            std::string tmp = std::ctime(&t);
            return tmp;
        }
        static void ListShow(const httplib::Request &req, httplib::Response &rsp){
            
            cout << "ListShow" << endl;

            std::vector<BackupInfo> arry;
            DataManager::GetInstance()->GetAll(&arry);
            std::stringstream ss;
            ss << "<html><head><title>Download</title></head>";
            ss << "<body><h1>Download</h1><table>";
            for(auto& a : arry){
                ss << "<tr>";
                std::string filename = FileUtil(a._real_path).FileName();
                ss << "<td><a href='" << a._url << "'>" << filename << "</a></td>";
                ss << "<td align='right'>" << TimetoStr(a._mtime) << "</td>";
                ss << "<td align='right'>" << a._fsize / 1024 << "k</td>";
                ss << "</tr>";
            }
            ss << "</table></body></html>";
            rsp.body = ss.str();
            rsp.set_header("Content-Type", "text/html");
            rsp.status = 200;
            return;
        }
        static std::string GetETag(const BackupInfo& info){
            FileUtil fu(info._real_path);
            std::string etag = fu.FileName();
            etag += "-";
            etag += std::to_string(info._fsize);
            etag += "-";
            etag += std::to_string(info._mtime);
            return etag;
        }
        static void DownLoad(const httplib::Request &req, httplib::Response &rsp){

            cout << "DownLoad" << endl;
            
            BackupInfo info;
            DataManager::GetInstance()->GetOneByUrl(req.path, &info);
            if(info._pack_flag == true){
                FileUtil fu(info._pack_path);
                fu.UnCompress(info._real_path);
                fu.Remove();
                info._pack_flag = false;
                DataManager::GetInstance()->Updata(info);
            }

            bool retrans = false;
            if(req.has_header("If-Range") == true){
                std::string old_etag = req.get_header_value("If-Range");
                if(old_etag == GetETag(info)){
                    retrans = true;
                }
            }

            FileUtil fu(info._real_path);
            if(retrans == false){
                fu.GetContent(rsp.body);
                rsp.set_header("Accept-Range", "bytes");
                rsp.set_header("ETag", GetETag(info));
                rsp.set_header("Content-Type", "application/octet-stream");
                rsp.status = 200;
            }
            else{
                fu.GetContent(rsp.body);
                rsp.set_header("Accept-Range", "bytes");
                rsp.set_header("ETag", GetETag(info));
                rsp.set_header("Content-Type", "application/octet-stream");
                //rsp.set_header("Content-Range", "bytes start-end/fsize");
                rsp.status = 206;
            }
        }
    public:
        Server(){
            Config* conf = Config::GetInstance();
            _server_port = conf->GetServerPort();
            _server_ip = conf->GetServerIp();
            _download_prefix = conf->GetDownloadPrefix();
        }
        bool RunModule(){
            _server.Post("/upload", Upload);
            _server.Get("/listshow", ListShow);
            _server.Get("/", ListShow);
            std::string download_url = _download_prefix + "(.*)";
            _server.Get(download_url, DownLoad);
            _server.listen(_server_ip.c_str(), _server_port);
            return true;
        }
    };
}